package wang.wangby.lazydoc.service;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.aop.SpringProxy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import lombok.extern.slf4j.Slf4j;
import wang.wangby.autoconfigure.lazydoc.LazyDocProperties;
import wang.wangby.lazydoc.common.DocRules;
import wang.wangby.lazydoc.model.ApiInfo;
import wang.wangby.utils.ClassUtil;

@Slf4j
public class LazyDocService {
	// 解析出来的接口信息
	private volatile List<ApiInfo> apiList;
	// 当缓存的时候，为了防止重复解析加的锁
	private ReentrantLock lock = new ReentrantLock();
	@Autowired
	private ApplicationContext content;

	@Autowired
	private LazyDocProperties properties;
	
	@Autowired
	private DocRules docRules;

	// 扫描系统所有Controller,解析所有设置了RequestMapping的方法
	public List<ApiInfo> getApiInfo() {
		if (apiList != null && properties.isCache()) {
			return apiList;
		}
		lock.lock();
		try {
			apiList = getApiInfoInternal();
		} finally {
			lock.unlock();
		}
		return apiList;

	}

	protected List<ApiInfo> getApiInfoInternal() {
		Map<String, Object> beans = content.getBeansWithAnnotation(Controller.class);
		List<ApiInfo> result = new ArrayList();
		beans.forEach((name, obj) -> {
			Class clazz = obj.getClass();
			List apis = parse(clazz);
			result.addAll(apis);
		});

		// 根据方法所在类排序
		Collections.sort(result, (info1, info2) -> {
			return info1.getName().compareTo(info2.getClass().getName());
		});
		return result;
	}

	private List parse(Class clazz) {
		// 如果是代理类就用父类
		if (ClassUtil.isInstance(clazz, SpringProxy.class)) {
			clazz = clazz.getSuperclass();
		}

		List<ApiInfo> list = new ArrayList();
		for (Method method : clazz.getMethods()) {
			// 根据requestMapping,判断是否是接口方法
			ApiInfo info = createByMapping(method);
			if (info == null) {
				continue;
			}
			list.add(info);
		}
		return list;
	}

	private ApiInfo createByMapping(Method method) {
		RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
		if(mapping==null) {
			return null;
		}
		ApiInfo info=new ApiInfo(docRules);
		info.setClazz(method.getDeclaringClass());
		info.setMethod(method);
		log.debug("找到接口:"+info.getName());
		return info;
	}

}
